#include "./cmd_uart.h"
#include "../xlib/dma.h"
#include "../xlib/gpio.h"
#include "../xlib/uart.h"

namespace driver {

CmdUart cmdUart;

namespace cmd_uart {

using namespace stm32f1;

using TX = gpio::Cfg<gpio::PA<9>, gpio::Mode::OUT_50MHz_AF_PP>;
using RX = gpio::Cfg<gpio::PA<10>, gpio::Mode::INPUT_FLOAT>;
using UART = uart::Uart1;
using DMA_TX = dma::DmaSel<UART::BASE_VAL>::TX_CHAN;
using DMA_RX = dma::DmaSel<UART::BASE_VAL>::RX_CHAN;

} // namespace cmd_uart
using namespace cmd_uart;

void CmdUart::init() {
    m_mq.init();
    m_flags.init();
    m_flags.set_flags(FLAG_DMA_TC);

    rcc::enable<TX, RX, UART, DMA_TX, DMA_RX>();
    gpio::config<RX, TX>();
    UART::set_cfg<uart::DefCfg>();
    DMA_RX::set_cfg<dma::UartRecvCfg, UART::DR_ADDR_VAL>(
        reinterpret_cast<uint32_t>(m_recv_buf), RECV_BUF_SIZE);
    NVIC_EnableIRQ(USART1_IRQn);
    NVIC_EnableIRQ(DMA1_Channel4_IRQn);
}

void CmdUart::write_by_dma(const void *buf, uint16_t len) {
    DMA_TX::set_cfg<dma::UartWriteCfg, UART::DR_ADDR_VAL>(
        reinterpret_cast<uint32_t>(buf), len);
}

void CmdUart::on_recv_idle() {
    uint32_t index = RECV_BUF_SIZE - DMA_RX::ndtr();
    if (index == m_recv_index) {
        return;
    }
    using namespace utils;
    if (index > m_recv_index) {
        ByteView bw(m_recv_buf + m_recv_index, index - m_recv_index);
        m_mq.post(bw);
    } else {
        ByteView bw(m_recv_buf + m_recv_index, RECV_BUF_SIZE - m_recv_index);
        m_mq.post(bw);
        if (index != 0) {
            bw.buf = m_recv_buf;
            bw.len = index;
            m_mq.post(bw);
        }
    }
    m_recv_index = index;
}


} // namespace driver

using namespace driver;

extern "C" void USART1_IRQHandler() {
    if (UART::is_idle()) {
        UART::clear_idle_flag();
        cmdUart.on_recv_idle();
    }
}

extern "C" void DMA1_Channel4_IRQHandler() {
    if (DMA_TX::is_tc()) {
        DMA_TX::clear_flags();
        cmdUart.on_dma_tc();
    }
}
